// Copyright (C) 2010 and onwards Noam Shazeer and Georges Harik
// Pieces of this code may be licenced under the Apache License, version 2.0 
//   from Harik Shazeer Labs

#ifndef _ECONOMY_H_
#define _ECONOMY_H_

#include <pthread.h>
#include "interpret.h"
#include "objects.h"
#include "nolock.h"
#include "problem.h"
#include "function-model.h"
#include "server.h"
#include "ads.h"

// Forward declarations
struct EconomyThread;
struct Economy;

// This structure is meant to assign credit for improvements to a utility
// score.  
// For an improvement in utility, we assign credit equal to the average
// amount of time necessary to produce such an improvement since a horizon.
// The horizon moves to include approximately the most recent history_fraction_
// of time since the creation of the CreditAssigner object.
struct CreditAssigner {
  // call to begin. 
  void Initialize(double time, double utility);

  // call whenever there is a new utility.
  // return an amount of credit to assign in units of time.
  double Credit(double time, double utility);

private:
  // recomputes the horizon. 
  void PushHorizon();

  bool ShouldPushHorizon();
  
  // All (time,utility) pairs ever inserted
  vector<pair<double, double> > history_;
  
  // index into the history array where recent history begins.
  uint64 horizon_;

  // what fraction of history to consider as recent history. 
  double horizon_fraction_;
};

struct EconomyThread;

void InitEconomy();

// This contains all the information necessary to run the economy
struct Economy : public AllocatorDependant {
  Economy();
  void Construct();

  // permanent and temporary ads.
  Ads::AdStore ad_store_;

  // These functions are called during garbage collection.   
  // Let's keep them here for now rather than moving them to EconomyThread
  
  void AboutToCollect();
  void FlagReferences(int32 shard, Frontier * frontier);
  int32 NumShards() { return 100; }
  
  void Store(string prefix);

  void RunCommand(string s);
  void ScheduleCommand(string s);
  void RunScheduledCommands();
  void CheckRunFile();
  void CheckCronFile();
  
  // timing for the economy
  Clock clock_;

  // commands that we should stop the system to run
  vector<string> commands_;
  map<string, uint64> last_run_; // last time each command was run. 
  pthread_mutex_t commands_lock_;
  
  RNG rng_;

  // SYNCHRONIZATION
  set<EconomyThread *> et_;

  uint64 num_threads_;
  bool sorting_ads_;

  void MakeThreads(int n);
  void StartThreads();
  void StartWebServerThread();

  // Testing
  Solution * solution_;
  int64 utility_;
  CreditAssigner assigner_;
  pthread_mutex_t lock_;
  pthread_mutex_t credit_lock_;

  uint64 accept_threshold_;
};

extern Economy * G_EC;

struct EconomyThread 
  : public System, public WebBackend { 
  EconomyThread();
  ~EconomyThread();

  void SetThreadAllocator(ThreadAllocator *a);

  int64 Payout();

  string EntityToString(Thing & entity);
  
  // bool IsNativeMethod(Thing name);
  Thing SystemCall(int32 which, int32 num_parameters);

  // 0 means loop forever
  void Loop(int64 max_cpu = 0); 
  void UpdateReferenceCounts(int delta);

  void REPL(istream & in);

  string Serve(WebRequest request);
  
  // Returns success.
  bool ReadInitialAd(istream& in, Thing *ad, 
		     uint64 *weight);
  
  void ReadInitialAds(string filename);

  // Picking a random number of cpu cycles to run something for. 
  int64 RandomTimeLimitNS();
  
  void PushAd(Thing ad, int64 weight);

  void FlushAds(bool force);

  void StoreIncome(Thing entity, double cpu);
  
  // Reading from incomed entities
  double AdjustTemporary(double temp_ns);
  bool ReadAd(Thing lhs, Thing *rhs);
  Thing ReadContributors(Thing lhs, Thing rhs, Thing end_time);

  // Running lisp code inside this thread
  Thing RunLispCode(Thing code = NULL);

  // does some periodic tasks
  void Epoch(bool force = false);
  
  int64 Sleep(int64 request);

  void Post(Thing ad, Thing cash);

  // TODO
  // Give credit without being on contributors list
  //Thing SyscallRepost
  //  (Thing lhs, Thing demand, Thing rhs, Thing end_time, Thing cash);

  // returns the additional income per contributor
  Thing Test(Thing contributors, Thing maybe_force = NULL);

  // return an ad in the given range. 
  // inclusive_start and inclusive_end are prefixes of ads.
  // strategy_thing is an integer corresponding to a SampleStrategy
  Thing SyscallSample(Thing inclusive_start,
		      Thing inclusive_end,
		      Thing strategy_thing);

  Thing SyscallRangeWeight(Thing inclusive_start, 
			   Thing inclusive_end);

  Thing SyscallRangeCount(Thing inclusive_start, 
			  Thing inclusive_end);

  Thing SyscallReadAll(Thing inclusive_start, 
		       Thing inclusive_end);


  // data
  Economy * e_;
  SolutionThread *tester_;
  Vinterpreter * vi_;
  ThreadAllocator * a_;
  RNG rng_;
  int64 clock_at_last_epoch_;

  // Which code is running
  Thing running_;  

  Ads::Sampler *local_ads_;
  StableVector<Ads::Sampler *> local_ads_q_; // built and waiting to be submitted
  RWLock::Reader * ad_store_reader_;
  uint64 local_ads_capacity_;
};

void * LoopEconomyThread(void *economy_thread);

#endif
